package com.goldgov.code.meta.source.impl;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;

import com.goldgov.code.meta.Constraint;
import com.goldgov.code.meta.FieldInfo;
import com.goldgov.code.meta.FieldType;
import com.goldgov.code.meta.ModuleInfo;
import com.goldgov.code.meta.semantics.SemanticsTranslate;
import com.goldgov.code.meta.source.MetaSource;
import com.goldgov.code.utils.StringUtils;
import com.goldgov.jdbc.service.JdbcConfig;
import com.goldgov.jdbc.service.JdbcConfigService;

public class JdbcMetaSource implements MetaSource {
	
	private final String configID;
	private final String[] tableName;
	private final JdbcConfigService jdbcConfigService;
	private SemanticsTranslate translate;
	
	public JdbcMetaSource(JdbcConfigService jdbcConfigService,String configID,String[] tableName) {
		this.jdbcConfigService = jdbcConfigService;
		this.configID = configID;
		this.tableName = tableName;
	}

	@Override
	public ModuleInfo[] resolve() {
		return resolve(null);
	}
	
	@Override
	public ModuleInfo[] resolve(String prefix) {
		if(prefix != null) {prefix = prefix.toUpperCase();}
		JdbcConfig config = jdbcConfigService.getConfig(configID);
		
		ModuleInfo[] moduleInfos = new ModuleInfo[tableName.length];
		
		try (Connection connection = DriverManager.getConnection(config.getUrl(), config.getUsername(), config.getPassword())){
			DatabaseMetaData metaData = connection.getMetaData();
			for (int i = 0; i < tableName.length; i++) {
				String tn = tableName[i];
				if(prefix != null && tn.toUpperCase().startsWith(prefix.toUpperCase())){
					tn = tn.substring(prefix.length());
				}
				ModuleInfo module = new ModuleInfo();
				module.setName(StringUtils.upperFirstChar(StringUtils.lowerCaseWithSeparator(tn, "_")));
				module.setPackageName(module.getName().toLowerCase());
				module.setTableName(tableName[i]);
				module.setExplain(translateField(tableName[i]));
				
				ResultSet primaryKeys = metaData.getPrimaryKeys(null, null, tableName[i]);
				String ids = "";
				while(primaryKeys.next()){
					ids += primaryKeys.getString("COLUMN_NAME")+",";
				}
				String[] idArray = ids.split(",");
				ResultSet columns = metaData.getColumns(null, null, tableName[i], null);
				
				List<FieldInfo> filedList = new ArrayList<>();
				while(columns.next()){
					String columnName = columns.getString("COLUMN_NAME");
					Integer dataType = columns.getInt("DATA_TYPE");
					String nullable = columns.getString("IS_NULLABLE"); 
					String remarks = columns.getString("REMARKS");
					
					FieldInfo fieldInfo = new FieldInfo();
					String attName = columnName;
					if(prefix != null && columnName.toUpperCase().startsWith(prefix)){
						attName = columnName.substring(prefix.length());
					}
					fieldInfo.setField(columnName);
					fieldInfo.setName(StringUtils.lowerCaseWithSeparator(attName, "_"));
					fieldInfo.setType(getType(dataType));
					fieldInfo.setExplain((remarks == null || "".equals(remarks))?translateField(columnName):remarks);
					
					Constraint constraint = new Constraint();
					if("NO".equals(nullable)){
						constraint.setRequired(true);
						for (String pk : idArray) {
							if(fieldInfo.getField().equals(pk)) {
								constraint.setPrimaryKey(true);
								break;
							}
						}
					}
					constraint.setControlType("Input");
					fieldInfo.setConstraint(constraint);
					filedList.add(fieldInfo);
				}
				module.setFields(filedList.toArray(new FieldInfo[0]));
				moduleInfos[i] = module;
			}
			
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return moduleInfos;
	}
		
	private FieldType getType(int type){
		switch (type) {
		case Types.CHAR:
		case Types.VARCHAR:
			return FieldType.VARCHAR;
		case Types.INTEGER:
		case Types.NUMERIC:
			return FieldType.INT;
		case Types.TIMESTAMP:
		case Types.DATE:
		case Types.TIME:
			return FieldType.DATETIME;
		case Types.DECIMAL:
			return FieldType.DECIMAL;
		case Types.LONGVARCHAR:
		case Types.CLOB:
			return FieldType.CLOB;
		case Types.LONGVARBINARY:
		case Types.BLOB:
			return FieldType.BLOB;
		default:
			return FieldType.VARCHAR;
		}
	}

	@Override
	public void setSemanticsTranslate(SemanticsTranslate translate) {
		this.translate = translate;
	}
	
	private String translateField(String text) {
		if(translate != null) {
			StringBuilder strBuilder = new StringBuilder();
			String[] splitInfo = text.split("_");
			boolean canTranslate = false;
			for (String str : splitInfo) {
				String translateResult = translate.translate(str);
				if(translateResult.equals(str)) {
					translateResult = StringUtils.upperFirstChar(str.toLowerCase());
				}
				strBuilder.append(translateResult);
				if(!canTranslate && !translateResult.equals(str)) {
					canTranslate = true;
				}
				
			}
			return canTranslate ? strBuilder.toString() : null;
		}
		return null;
	}
		
}

	